home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / status.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  35KB  |  1,251 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: status.c,v 4.81 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      status.c
  44.      Functions that manage the status line (third from the bottom)
  45.        - put messages on the queue to be displayed
  46.        - display messages on the queue with timers 
  47.        - check queue to figure out next timeout
  48.        - prompt for yes/no type of questions
  49.   ====*/
  50.  
  51. #include "headers.h"
  52.  
  53. /*
  54.  * Internal queue of messages.  The circular, double-linked list's 
  55.  * allocated on demand, and cleared as each message is displayed.
  56.  */
  57. typedef struct message {
  58.     char        text[MAX_SCREEN_COLS+1];
  59.     unsigned        flags:8;
  60.     unsigned        shown:1;
  61.     int            min_display_time, max_display_time;
  62.     struct message *next, *prev;
  63. } SMQ_T;
  64.  
  65. #define    LAST_MESSAGE(X)    ((X) == (X)->next)
  66. #define    RAD_BUT_COL    0
  67.  
  68.  
  69. /*
  70.  * Internal prototypes
  71.  */
  72. int  output_message PROTO((SMQ_T *));
  73. void radio_help PROTO((int, int, HelpType));
  74. void draw_radio_prompt PROTO((int, int, char *));
  75. void pause_for_current_message PROTO(());
  76. int  messages_in_queue PROTO(());
  77. void delay_cmd_cue PROTO((int));
  78.  
  79.  
  80.  
  81. /*----------------------------------------------------------------------
  82.      Manage the second line from the bottom where status and error messages
  83. are displayed. A small queue is set up and messages are put on the queue
  84. by calling one of the q_status_message routines. Even though this is a queue
  85. most of the time message will go right on through. The messages are 
  86. displayed just before the read for the next command, or when a read times
  87. out. Read timeouts occur every minute or so for new mail checking and every
  88. few seconds when there are still messages on the queue. Hopefully, this scheme 
  89. will not let messages fly past that the user can't see.
  90.   ----------------------------------------------------------------------*/
  91.  
  92.  
  93. static SMQ_T *message_queue = NULL;
  94. static short  needs_clearing = 0, /* Flag set by want_to()
  95.                                               and optionally_enter() */
  96.           prevstartcol;
  97. static char   prevstatusbuff[MAX_SCREEN_COLS+1];
  98. static time_t displayed_time;
  99.  
  100.  
  101. /*----------------------------------------------------------------------
  102.         Put a message for the status line on the queue
  103.  
  104.   Args: time    -- the min time in seconds to display the message
  105.         message -- message string
  106.  
  107.   Result: queues message on queue represented by static variables
  108.  
  109.     This puts a single message on the queue to be shown.
  110.   ----------*/
  111. void
  112. q_status_message(flags, min_time, max_time, message)
  113.     int   flags;
  114.     int   min_time,max_time;
  115.     char *message;
  116. {
  117.     SMQ_T *new;
  118.  
  119.     if((flags & SM_INFO) && message_queue)
  120.       return;
  121.  
  122.     new = (SMQ_T *)fs_get(sizeof(SMQ_T));
  123.     memset(new, 0, sizeof(SMQ_T));
  124.     strncpy(new->text, message, MAX_SCREEN_COLS);
  125.     new->min_display_time = min_time;
  126.     new->max_display_time = max_time;
  127.     new->flags            = flags;
  128.     if(message_queue){
  129.     new->next = message_queue;
  130.     new->prev = message_queue->prev;
  131.     new->prev->next = message_queue->prev = new;
  132.     }
  133.     else
  134.       message_queue = new->next = new->prev = new;
  135.  
  136.     dprint(9, (debugfile, "q_status_message(%.40s)\n", message));
  137. }
  138.  
  139.  
  140. /*----------------------------------------------------------------------
  141.         Put a message with 1 printf argument on queue for status line
  142.  
  143.     Args: min_t -- minimum time to display message for
  144.           max_t -- minimum time to display message for
  145.           s -- printf style control string
  146.           a -- argument for printf
  147.  
  148.    Result: message queued
  149.   ----*/
  150.  
  151. /*VARARGS1*/
  152. void
  153. q_status_message1(flags, min_t, max_t, s, a)
  154.     int      flags;
  155.     int   min_t, max_t;
  156.     char *s;
  157.     void *a;
  158. {
  159.     char buf[1000];
  160.  
  161.     sprintf(buf, s, a);
  162.     q_status_message(flags, min_t, max_t, buf);
  163. }
  164.  
  165.  
  166.  
  167. /*----------------------------------------------------------------------
  168.         Put a message with 2 printf argument on queue for status line
  169.  
  170.     Args: min_t  -- minimum time to display message for
  171.           max_t  -- maximum time to display message for
  172.           s  -- printf style control string
  173.           a1 -- argument for printf
  174.           a2 -- argument for printf
  175.  
  176.   Result: message queued
  177.   ---*/
  178.  
  179. /*VARARGS1*/
  180. void
  181. q_status_message2(flags, min_t, max_t, s, a1, a2)
  182.     int   flags;
  183.     int   min_t, max_t;
  184.     char *s;
  185.     void *a1, *a2;
  186. {
  187.     char buf[1000];
  188.  
  189.     sprintf(buf, s, a1, a2);
  190.     q_status_message(flags, min_t, max_t, buf);
  191. }
  192.  
  193.  
  194.  
  195. /*----------------------------------------------------------------------
  196.         Put a message with 3 printf argument on queue for status line
  197.  
  198.     Args: min_t  -- minimum time to display message for
  199.           max_t  -- maximum time to display message for
  200.           s  -- printf style control string
  201.           a1 -- argument for printf
  202.           a2 -- argument for printf
  203.           a3 -- argument for printf
  204.  
  205.   Result: message queued
  206.   ---*/
  207.  
  208. /*VARARGS1*/
  209. void
  210. q_status_message3(flags, min_t, max_t, s, a1, a2, a3)
  211.     int   flags;
  212.     int   min_t, max_t;
  213.     char *s;
  214.     void *a1, *a2, *a3;
  215. {
  216.     char buf[1000];
  217.  
  218.     sprintf(buf, s, a1, a2, a3);
  219.     q_status_message(flags, min_t, max_t, buf);
  220. }
  221.  
  222.  
  223.  
  224. /*----------------------------------------------------------------------
  225.         Put a message with 4 printf argument on queue for status line
  226.  
  227.  
  228.     Args: min_t  -- minimum time to display message for
  229.           max_t  -- maximum time to display message for
  230.           s  -- printf style control string
  231.           a1 -- argument for printf
  232.           a2 -- argument for printf
  233.           a3 -- argument for printf
  234.           a4 -- argument for printf
  235.  
  236.   Result: message queued
  237.   ----------------------------------------------------------------------*/
  238. /*VARARGS1*/
  239. void
  240. q_status_message4(flags, min_t, max_t, s, a1, a2, a3, a4)
  241.     int   flags;
  242.     int   min_t, max_t;
  243.     char *s;
  244.     void *a1, *a2, *a3, *a4;
  245. {
  246.     char buf[1000];
  247.  
  248.     sprintf(buf, s, a1, a2, a3, a4);
  249.     q_status_message(flags, min_t, max_t, buf);
  250. }
  251.  
  252.  
  253. /*----------------------------------------------------------------------
  254.         Put a message with 7 printf argument on queue for status line
  255.  
  256.  
  257.     Args: min_t  -- minimum time to display message for
  258.           max_t  -- maximum time to display message for
  259.           s  -- printf style control string
  260.           a1 -- argument for printf
  261.           a2 -- argument for printf
  262.           a3 -- argument for printf
  263.           a4 -- argument for printf
  264.           a5 -- argument for printf
  265.           a6 -- argument for printf
  266.           a7 -- argument for printf
  267.  
  268.  
  269.   Result: message queued
  270.   ----------------------------------------------------------------------*/
  271. /*VARARGS1*/
  272. void
  273. q_status_message7(flags, min_t, max_t, s, a1, a2, a3, a4, a5, a6, a7)
  274.     int   flags;
  275.     int   min_t, max_t;
  276.     char *s;
  277.     void *a1, *a2, *a3, *a4, *a5, *a6, *a7;
  278. {
  279.     char buf[1000];
  280.  
  281.     sprintf(buf, s, a1, a2, a3, a4, a5, a6, a7);
  282.     q_status_message(flags, min_t, max_t, buf);
  283. }
  284.  
  285.  
  286. /*VARARGS1*/
  287. void
  288. q_status_message8(flags, min_t, max_t, s, a1, a2, a3, a4, a5, a6, a7, a8)
  289.     int   flags;
  290.     int   min_t, max_t;
  291.     char *s;
  292.     void *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
  293. {
  294.     char buf[1000];
  295.  
  296.     sprintf(buf, s, a1, a2, a3, a4, a5, a6, a7, a8);
  297.     q_status_message(flags, min_t, max_t, buf);
  298. }
  299.  
  300.  
  301. /*----------------------------------------------------------------------
  302.      Mark the status line as dirty so it gets cleared next chance
  303.  ----*/
  304. void
  305. mark_status_dirty()
  306. {
  307.     mark_status_unknown();
  308.     needs_clearing++;
  309. }
  310.  
  311.  
  312. /*----------------------------------------------------------------------
  313.     Cause status line drawing optimization to be turned off, because we
  314.     don't know what the status line looks like.
  315.  ----*/
  316. void
  317. mark_status_unknown()
  318. {
  319.     prevstartcol = -1;
  320.     prevstatusbuff[0]  = '\0';
  321. }
  322.  
  323.  
  324.  
  325. /*----------------------------------------------------------------------
  326.      Wait a suitable amount of time for the currently displayed message
  327.  ----*/
  328. void
  329. pause_for_current_message()
  330. {
  331.     if(message_queue){
  332.     int w = (int)(displayed_time-time(0))+message_queue->min_display_time;
  333.     if(w > 0){
  334.         delay_cmd_cue(1);
  335.         sleep(w);
  336.         delay_cmd_cue(0);
  337.     }
  338.  
  339.     d_q_status_message();
  340.     }
  341. }
  342.  
  343.  
  344.  
  345. /*----------------------------------------------------------------------
  346.         Find out how many messages are queued for display
  347.  
  348.   Args:   dtime -- will get set to minimum display time for current message
  349.  
  350.   Result: number of messages in the queue.
  351.  
  352.   ---------*/
  353. int
  354. messages_queued(dtime)
  355.     long *dtime;
  356. {
  357.     if(message_queue && dtime)
  358.       *dtime = (long)max(message_queue->min_display_time, 1L);
  359.  
  360.     return((ps_global->in_init_seq) ? 0 : messages_in_queue());
  361. }
  362.  
  363.  
  364.  
  365. /*----------------------------------------------------------------------
  366.        Return number of messages in queue
  367.   ---------*/
  368. int
  369. messages_in_queue()
  370. {
  371.     int       n = message_queue ? 1 : 0;
  372.     SMQ_T *p = message_queue;
  373.  
  374.     while(n && (p = p->next) != message_queue)
  375.       n++;
  376.  
  377.     return(n);
  378. }
  379.  
  380.  
  381.  
  382. /*----------------------------------------------------------------------
  383.        Update status line, clearing or displaying a message
  384.  
  385.    Arg: command -- The command that is about to be executed
  386.  
  387.   Result: status line cleared or
  388.              next message queued is displayed or
  389.              current message is redisplayed.
  390.          if next message displayed, it's min display time
  391.          is returned else if message already displayed, it's
  392.          time remaining on the display is returned, else 0.
  393.  
  394.    This is called when ready to display the next message, usually just
  395. before reading the next command from the user. We pass in the nature
  396. of the command because it affects what we do here. If the command just
  397. executed by the user is a redraw screen, we don't want to reset or go to 
  398. next message because it might not have been seen.  Also if the command
  399. is just a noop, which are usually executed when checking for new mail 
  400. and happen every few minutes, we don't clear the message.
  401.  
  402. If it was really a command and there's nothing more to show, then we
  403. clear, because we know the user has seen the message. In this case the
  404. user might be typing commands very quickly and miss a message, so
  405. there is a time stamp and time check that each message has been on the
  406. screen for a few seconds.  If it hasn't we just return and let it be
  407. taken care of next time.
  408.  
  409. At slow terminal output speeds all of this can be for naught, the amount
  410. of time it takes to paint the screen when the whole screen is being painted
  411. is greater than the second or two delay so the time stamps set here have
  412. nothing to do with when the user actually sees the message.
  413. ----------------------------------------------------------------------*/
  414. int
  415. display_message(command)
  416.     int command;
  417. {
  418.     if(ps_global == NULL || ps_global->ttyo == NULL
  419.        || ps_global->ttyo->screen_rows <= 1 || ps_global->in_init_seq)
  420.       return(0);
  421.  
  422.     /*---- Deal with any previously displayed messages ----*/
  423.     if(message_queue && message_queue->shown) {
  424.     if(command == ctrl('L')) {    /* just repaint it, and go on */
  425.         mark_status_unknown();
  426.         mark_keymenu_dirty();
  427.         mark_titlebar_dirty();
  428.         output_message(message_queue);
  429.         return(0);
  430.     }
  431.     else {                /* ensure sufficient time's passed */
  432.         time_t now;
  433.         int    diff;
  434.  
  435.         now  = time(0);
  436.         diff = (int)(displayed_time - now)
  437.             + ((command == NO_OP_COMMAND || command == NO_OP_IDLE)
  438.                 ? message_queue->max_display_time
  439.                 : message_queue->min_display_time);
  440.             dprint(9, (debugfile,
  441.                "STATUS: diff:%d, displayed: %ld, now: %ld\n",
  442.                diff, displayed_time, now));
  443.             if(diff > 0)
  444.               return(diff);            /* check again next time  */
  445.         else if(LAST_MESSAGE(message_queue)
  446.             && (command == NO_OP_COMMAND || command == NO_OP_IDLE)
  447.             && message_queue->max_display_time)
  448.           return(0);            /* last msg, no cmd, has max */
  449.  
  450.         d_q_status_message();        /* remove it from queue and */
  451.         needs_clearing++;            /* clear the line if needed */
  452.     }
  453.     }
  454.  
  455.     if(!message_queue && (command == ctrl('L') || needs_clearing)) {
  456.     dprint(9, (debugfile, "Clearing status line\n"));
  457.     ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
  458.     mark_status_unknown();
  459.     if(command == ctrl('L')){
  460.         mark_keymenu_dirty();
  461.         mark_titlebar_dirty();
  462.     }
  463.     }
  464.  
  465.     /*---- Display any queued messages, weeding 0 disp times ----*/
  466.     while(message_queue && !message_queue->shown)
  467.       if(message_queue->min_display_time || LAST_MESSAGE(message_queue)){
  468.       displayed_time = time(0);
  469.       output_message(message_queue);
  470.       }
  471.       else
  472.     d_q_status_message();
  473.  
  474.     needs_clearing = 0;                /* always cleared or written */
  475.     dprint(9, (debugfile,
  476.                "STATUS cmd:%d, max:%d, min%d\n", command, 
  477.            (message_queue) ? message_queue->max_display_time : -1,
  478.            (message_queue) ? message_queue->min_display_time : -1));
  479.     fflush(stdout);
  480.     return(0);
  481. }
  482.  
  483.  
  484.  
  485. /*----------------------------------------------------------------------
  486.      Display all the messages on the queue as quickly as possible
  487.   ----*/
  488. void
  489. flush_status_messages(skip_last_pause)
  490.     int skip_last_pause;
  491. {
  492.     while(message_queue){
  493.     if(LAST_MESSAGE(message_queue)
  494.        && skip_last_pause
  495.        && message_queue->shown)
  496.       break;
  497.  
  498.     if(message_queue->shown)
  499.       pause_for_current_message();
  500.  
  501.     while(message_queue && !message_queue->shown)
  502.       if(message_queue->min_display_time
  503.          || LAST_MESSAGE(message_queue)){
  504.           displayed_time = time(0);
  505.           output_message(message_queue);
  506.       }
  507.       else
  508.         d_q_status_message();
  509.     }
  510. }
  511.  
  512.  
  513.  
  514. /*----------------------------------------------------------------------
  515.      Make sure any and all SM_ORDER messages get displayed.
  516.  
  517.      Note: This flags the message line as having nothing displayed.
  518.            The idea is that it's a function called by routines that want
  519.        the message line for a prompt or something, and that they're
  520.        going to obliterate the message anyway.
  521.  ----*/
  522. void
  523. flush_ordered_messages()
  524. {
  525.     SMQ_T *start = NULL;
  526.  
  527.     while(message_queue && message_queue != start){
  528.     if(message_queue->shown)
  529.       pause_for_current_message(); /* changes "message_queue" */
  530.  
  531.     while(message_queue && !message_queue->shown
  532.           && message_queue != start)
  533.       if(message_queue->flags & SM_ORDER){
  534.           if(message_queue->min_display_time){
  535.           displayed_time = time(0);
  536.           output_message(message_queue);
  537.           }
  538.           else
  539.         d_q_status_message();
  540.       }
  541.       else if(!start)
  542.         start = message_queue;
  543.     }
  544. }
  545.  
  546.  
  547.      
  548. /*----------------------------------------------------------------------
  549.       Remove a message from the message queue.
  550.   ----*/
  551. void
  552. d_q_status_message()
  553. {
  554.     if(message_queue){
  555.     dprint(9, (debugfile, "d_q_status_message(%.40s)\n",
  556.            message_queue->text));
  557.     if(!LAST_MESSAGE(message_queue)){
  558.         SMQ_T *p = message_queue;
  559.         p->next->prev = p->prev;
  560.         message_queue = p->prev->next = p->next;
  561.         fs_give((void **)&p);
  562.     }
  563.     else
  564.       fs_give((void **)&message_queue);
  565.     }
  566. }
  567.  
  568.  
  569.  
  570. /*----------------------------------------------------------------------
  571.     Actually output the message to the screen
  572.  
  573.   Args: message            -- The message to output
  574.     from_alarm_handler -- Called from alarm signal handler.
  575.                   We don't want to add this message to the review
  576.                   message list since we may mess with the malloc
  577.                   arena here, and the interrupt may be from
  578.                   the middle of something malloc'ing.
  579.  ----*/
  580. int
  581. status_message_write(message, from_alarm_handler)
  582.     char *message;
  583.     int   from_alarm_handler;
  584. {
  585.     int  col, row, max_length, invert;
  586.     char obuff[MAX_SCREEN_COLS + 1];
  587.  
  588.     if(!from_alarm_handler)
  589.       add_review_message(message);
  590.  
  591.     invert = !InverseState();    /* already in inverse? */
  592.     row = max(0, ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
  593.  
  594.     /* Put [] around message and truncate to screen width */
  595.     max_length = ps_global->ttyo != NULL ? ps_global->ttyo->screen_cols : 80;
  596.     max_length = min(max_length, MAX_SCREEN_COLS);
  597.     obuff[0] = '[';
  598.     obuff[1] = '\0';
  599.     strncat(obuff, message, max_length - 2);
  600.     obuff[max_length - 1] = '\0';
  601.     strcat(obuff, "]");
  602.  
  603.     if(prevstartcol == -1 || strcmp(obuff, prevstatusbuff)){
  604.     /*
  605.      * Try to optimize drawing in this case.  If the length of the string
  606.      * changed, it is very likely a lot different, so probably not
  607.      * worth looking at.  Just go down the two strings drawing the
  608.      * characters that have changed.
  609.      */
  610.     if(prevstartcol != -1 && strlen(obuff) == strlen(prevstatusbuff)){
  611.         char *p, *q, *uneq_str;
  612.         int   column, start_col;
  613.  
  614.         if(invert)
  615.           StartInverse();
  616.  
  617.         q = prevstatusbuff;
  618.         p = obuff;
  619.         col = column = prevstartcol;
  620.  
  621.         while(*q){
  622.         /* skip over string of equal characters */
  623.         while(*q && *p == *q){
  624.             q++;
  625.             p++;
  626.             column++;
  627.         }
  628.  
  629.         if(!*q)
  630.           break;
  631.  
  632.         uneq_str  = p;
  633.         start_col = column;
  634.  
  635.         /* find end of string of unequal characters */
  636.         while(*q && *p != *q){
  637.             *q++ = *p++;  /* update prevstatusbuff */
  638.             column++;
  639.         }
  640.  
  641.         /* tie off and draw the changed chars */
  642.         *p = '\0';
  643.         PutLine0(row, start_col, uneq_str);
  644.  
  645.         if(*q){
  646.             p++;
  647.             q++;
  648.             column++;
  649.         }
  650.         }
  651.  
  652.         if(invert)
  653.           EndInverse();
  654.  
  655.         /* move cursor to a consistent position */
  656.         MoveCursor(row, 0);
  657.         fflush(stdout);
  658.     }
  659.     else{
  660.         ClearLine(row);
  661.         if(invert)
  662.           StartInverse();
  663.  
  664.         col = Centerline(row, obuff);
  665.         if(invert)
  666.           EndInverse();
  667.  
  668.         MoveCursor(row, 0);
  669.         fflush(stdout);
  670.         strcpy(prevstatusbuff, obuff);
  671.         prevstartcol = col;
  672.     }
  673.     }
  674.     else
  675.       col = prevstartcol;
  676.  
  677.     return(col);
  678. }
  679.  
  680.  
  681.  
  682. /*----------------------------------------------------------------------
  683.     Write the given status message to the display.
  684.  
  685.   Args: mq_entry -- pointer to message queue entry to write.
  686.  
  687.  ----*/
  688. int 
  689. output_message(mq_entry)
  690.     SMQ_T *mq_entry;
  691. {
  692.     int rv;
  693.  
  694.     dprint(9, (debugfile, "output_message(%s)\n", mq_entry->text));
  695.     rv = status_message_write(mq_entry->text, 0);
  696.  
  697.     if((mq_entry->flags & SM_DING) && F_OFF(F_QUELL_BEEPS, ps_global)){
  698.     Writechar(BELL, 0);            /* ring bell */
  699.     fflush(stdout);                /* and make sure they see it */
  700.     }
  701.  
  702.     /*
  703.      * If we're a modal message, loop waiting for acknowlegment
  704.      * *and* keep the stream alive.
  705.      */
  706.     if((mq_entry->flags & SM_MODAL) && !mq_entry->shown){
  707.     static char *modal_msg =
  708.          "* * * Read message above then hit Return to continue Pine * * *";
  709.     int ch;
  710.  
  711.     ClearLine(ps_global->ttyo->screen_rows-2);
  712.     MoveCursor(ps_global->ttyo->screen_rows-2,
  713.            max((ps_global->ttyo->screen_rows-sizeof(modal_msg))/2, 0));
  714.     Write_to_screen(modal_msg);
  715.     ClearLine(ps_global->ttyo->screen_rows-1);
  716.     mark_keymenu_dirty();
  717.  
  718.     while(1){
  719.         flush_input();
  720.         MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
  721.         if((ch = read_char(600)) == NO_OP_COMMAND || ch == NO_OP_IDLE)
  722.           new_mail(0, 2, 0);        /* wake up and prod server */
  723.         else if(ch != ctrl('M'))
  724.           Writechar(BELL, 0);        /* complain */
  725.         else
  726.           break;                /* done. */
  727.     }
  728.  
  729.     if(FOOTER_ROWS(ps_global) != 1){
  730.         ClearLine(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global));
  731.         if(!ps_global->mangled_footer)
  732.           redraw_keymenu();            /* else repaint next pass... */
  733.     }
  734.     else if(ps_global->redrawer)
  735.       (*ps_global->redrawer)();
  736.     }
  737.     else if(!(mq_entry->flags & SM_MODAL) && ps_global->status_msg_delay){
  738.     MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
  739.     fflush(stdout);
  740.     sleep(ps_global->status_msg_delay);
  741.     }
  742.  
  743.     mq_entry->shown = 1;
  744.     return(rv);
  745. }
  746.  
  747.  
  748.  
  749. /*----------------------------------------------------------------------
  750.     Write or clear delay cue
  751.  
  752.   Args: on -- whether to turn it on or not
  753.  
  754.  ----*/
  755. void
  756. delay_cmd_cue(on)
  757.     int on;
  758. {
  759.     int l;
  760.  
  761.     if(prevstartcol >= 0 && (l = strlen(prevstatusbuff))){
  762.     MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
  763.            prevstartcol ? max(prevstartcol - 1, 0) : 0);
  764.     StartInverse();
  765.     Write_to_screen(on ? "[>" : " [");
  766.     EndInverse();
  767.     MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
  768.            min(prevstartcol + l, ps_global->ttyo->screen_cols) - 1);
  769.     StartInverse();
  770.     Write_to_screen(on ? "<]" : "] ");
  771.     EndInverse();
  772.     MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0);
  773.     }
  774.  
  775.     fflush(stdout);
  776. #ifdef    _WINDOWS
  777.     mswin_setcursor ((on) ? MSWIN_CURSOR_BUSY : MSWIN_CURSOR_ARROW);
  778. #endif
  779. }
  780.  
  781.  
  782.  
  783. /*
  784.  * want_to's array passed to radio_buttions...
  785.  */
  786. static ESCKEY_S yorn[] = {
  787.     {'y', 'y', "Y", "Yes"},
  788.     {'n', 'n', "N", "No"},
  789.     {-1, 0, NULL, NULL}
  790. };
  791.  
  792.  
  793. /*----------------------------------------------------------------------
  794.      Ask a yes/no question in the status line
  795.  
  796.    Args: question     -- string to prompt user with
  797.          dflt         -- The default answer to the question (should probably
  798.              be y or n)
  799.          on_ctrl_C    -- Answer returned on ^C
  800.      help         -- Two line help text
  801.      display_help -- If true, display help without being asked
  802.  
  803.  Result: Messes up the status line,
  804.          returns y, n, dflt, or dflt_C
  805.   ---*/
  806. int
  807. want_to(question, dflt, on_ctrl_C, help, display_help, flush)
  808.     char    *question;
  809.     HelpType    help;
  810.     int        dflt, on_ctrl_C, display_help, flush;
  811. {
  812.     char *q2;
  813.     int      rv;
  814.  
  815. #ifdef _WINDOWS
  816.     if (mswin_usedialog ()) {
  817.     mswin_flush ();
  818.     switch (mswin_yesno (question)) {
  819.     default:
  820.     case 0:        return (on_ctrl_C);
  821.     case 1:        return ('y');
  822.     case 2:        return ('n');
  823.         }
  824.     }
  825. #endif
  826.  
  827.     /*----
  828.        One problem with adding the (y/n) here is that shrinking the 
  829.        screen while in radio_buttons() will cause it to get chopped
  830.        off. It would be better to truncate the question passed in
  831.        hear and leave the full "(y/n) [x] : " on.
  832.       ----*/
  833.     q2 = fs_get(strlen(question) + 6);
  834.     sprintf(q2, "%.*s? ", ps_global->ttyo->screen_cols - 6, question);
  835.     if(on_ctrl_C == 'n')    /* don't ever let cancel == 'n' */
  836.       on_ctrl_C = 0;
  837.  
  838.     rv = radio_buttons(q2,
  839.     (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
  840.     yorn, dflt, on_ctrl_C, help, flush ? RB_FLUSH_IN : RB_NORM);
  841.     fs_give((void **)&q2);
  842.  
  843.     return(rv);
  844. }
  845.  
  846.  
  847. int
  848. one_try_want_to(question, dflt, on_ctrl_C, help, display_help, flush)
  849.     char      *question;
  850.     HelpType   help;
  851.     int    dflt, on_ctrl_C, display_help, flush;
  852. {
  853.     char     *q2;
  854.     int          rv;
  855.  
  856.     q2 = fs_get(strlen(question) + 6);
  857.     sprintf(q2, "%.*s? ", ps_global->ttyo->screen_cols - 6, question);
  858.     rv = radio_buttons(q2,
  859.     (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
  860.     yorn, dflt, on_ctrl_C, help,
  861.         (flush ? RB_FLUSH_IN : RB_NORM) | RB_ONE_TRY);
  862.     fs_give((void **)&q2);
  863.  
  864.     return(rv);
  865. }
  866.  
  867.  
  868.  
  869. /*----------------------------------------------------------------------
  870.     Prompt user for a choice among alternatives
  871.  
  872. Args --  prompt:    The prompt for the question/selection
  873.          line:      The line to prompt on, if negative then relative to bottom
  874.          esc_list:  ESC_KEY_S list of keys
  875.          dflt:        The selection when the <CR> is pressed (should probably
  876.               be one of the chars in esc_list)
  877.          on_ctrl_C: The selection when ^C is pressed
  878.          help_text: Text to be displayed on bottom two lines
  879.      flags:     Logically OR'd flags modifying our behavior to:
  880.         RB_FLUSH_IN    - Discard any pending input chars.
  881.         RB_ONE_TRY     - Only give one chance to answer.  Returns
  882.                  on_ctrl_C value if not answered acceptably
  883.                  on first try.
  884.         RB_NO_NEWMAIL  - Quell the usual newmail check.
  885.     
  886.      Note: If there are enough keys in the esc_list to need a second
  887.            screen, and there is no help, then the 13th key will be
  888.            put in the help position.
  889.  
  890. Result -- Returns the letter pressed. Will be one of the characters in the
  891.           esc_list argument or one of the two deefaults.
  892.  
  893. This will pause for any new status message to be seen and then prompt the user.
  894. The prompt will be truncated to fit on the screen. Redraw and resize are
  895. handled along with ^Z suspension. Typing ^G will toggle the help text on and
  896. off. Character types that are not buttons will result in a beep (unless one_try
  897. is set).
  898.   ----*/
  899. int
  900. radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C, help_text, flags)
  901.     char     *prompt;
  902.     int          line;
  903.     ESCKEY_S *esc_list;
  904.     int       dflt;
  905.     int       on_ctrl_C;
  906.     HelpType  help_text;
  907.     int          flags;
  908. {
  909.     register int     ch, real_line;
  910.     char            *q, *ds = NULL;
  911.     int              cursor_moved, max_label, i, start, fkey_table[12];
  912.     int             km_popped = 0;
  913.     struct key         rb_keys[12];
  914.     struct key_menu  rb_keymenu;
  915.     bitmap_t         bitmap;
  916.  
  917. #ifdef _WINDOWS
  918.     if (mswin_usedialog ()) {
  919.     MDlgButton        button_list[12];
  920.     int            b;
  921.     int            i;
  922.     int            ret;
  923.     char            **help;
  924.  
  925.     memset (&button_list, 0, sizeof (MDlgButton) * 12);
  926.     b = 0;
  927.     for (i = 0; esc_list && esc_list[i].ch != -1 && i < 11; ++i) {
  928.         button_list[b].ch = esc_list[i].ch;
  929.         button_list[b].rval = esc_list[i].rval;
  930.         button_list[b].name = esc_list[i].name;
  931.         button_list[b].label = esc_list[i].label;
  932.         ++b;
  933.     }
  934.     button_list[b].ch = -1;
  935.     
  936.     help = get_help_text (help_text, NULL);
  937.  
  938.     ret = mswin_select (prompt, button_list, dflt, on_ctrl_C, 
  939.             help, flags);
  940.     if (help != NULL) 
  941.         free_help_text (help);
  942.     return (ret);
  943.     }
  944. #endif
  945.  
  946.     suspend_busy_alarm();
  947.     flush_ordered_messages();        /* show user previous status msgs */
  948.     mark_status_dirty();        /* clear message next display call */
  949.     real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
  950.     MoveCursor(real_line, RAD_BUT_COL);
  951.     CleartoEOLN();
  952.  
  953.     /*---- Find longest label ----*/
  954.     max_label = 0;
  955.     for(i = 0; esc_list && esc_list[i].ch != -1 && i < 12; i++){
  956.       if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
  957.     continue;
  958.       max_label = max(max_label, strlen(esc_list[i].name));
  959.     }
  960.  
  961.     q = cpystr(prompt); /* So we can truncate string below if need be */
  962.     if(strlen(q) + max_label > ps_global->ttyo->screen_cols) 
  963.         q[ps_global->ttyo->screen_cols - max_label] = '\0';
  964.  
  965.     /*---- Init structs for keymenu ----*/
  966.     for(i = 0; i < 12; i++)
  967.       memset((void *)&rb_keys[i], 0, sizeof(struct key));
  968.  
  969.     memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
  970.     rb_keymenu.how_many = 1;
  971.     rb_keymenu.keys     = rb_keys;
  972.  
  973.     /*---- Setup key menu ----*/
  974.     start = 0;
  975.     clrbitmap(bitmap);
  976.     memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
  977.     if(help_text != NO_HELP){        /* if shown, always at position 0 */
  978.     rb_keymenu.keys[0].name  = "?";
  979.     rb_keymenu.keys[0].label = "Help";
  980.     setbitn(0, bitmap);
  981.     fkey_table[0] = ctrl('G');
  982.     start++;
  983.     }
  984.  
  985.     if(on_ctrl_C){            /* if shown, always at position 1 */
  986.     rb_keymenu.keys[1].name  = "^C";
  987.     rb_keymenu.keys[1].label = "Cancel";
  988.     setbitn(1, bitmap);
  989.     fkey_table[1] = ctrl('C');
  990.     start++;
  991.     }
  992.  
  993.     start = (start) ? 2 : 0;
  994.     /*---- Show the usual possible keys ----*/
  995.     for(i=start; esc_list && esc_list[i-start].ch != -1 && i <= 12; i++){
  996.     /*
  997.      * If we have an esc_list item we'd like to put in the non-existent
  998.      * 13th slot, and there is no help, we put it in the help slot
  999.      * instead.  We're hacking now...!
  1000.      */
  1001.     if(i == 12){
  1002.         if(help_text != NO_HELP)
  1003.           panic("Programming botch in radio_buttons(): too many keys");
  1004.  
  1005.         if(esc_list[i-start].ch != -2)
  1006.           setbitn(0, bitmap); /* the help slot */
  1007.  
  1008.         fkey_table[0] = esc_list[i-start].ch;
  1009.         rb_keymenu.keys[0].name  = esc_list[i-start].name;
  1010.         if(esc_list[i-start].rval == dflt){
  1011.         ds = (char *)fs_get((strlen(esc_list[i-start].label) + 3)
  1012.                     * sizeof(char));
  1013.         sprintf(ds, "[%s]", esc_list[i-start].label);
  1014.         rb_keymenu.keys[0].label = ds;
  1015.         }
  1016.         else
  1017.           rb_keymenu.keys[0].label = esc_list[i-start].label;
  1018.     }
  1019.     else{
  1020.         if(esc_list[i-start].ch != -2)
  1021.           setbitn(i, bitmap);
  1022.  
  1023.         fkey_table[i] = esc_list[i-start].ch;
  1024.         rb_keymenu.keys[i].name  = esc_list[i-start].name;
  1025.         if(esc_list[i-start].rval == dflt){
  1026.         ds = (char *)fs_get((strlen(esc_list[i-start].label) + 3)
  1027.                     * sizeof(char));
  1028.         sprintf(ds, "[%s]", esc_list[i-start].label);
  1029.         rb_keymenu.keys[i].label = ds;
  1030.         }
  1031.         else
  1032.           rb_keymenu.keys[i].label = esc_list[i-start].label;
  1033.     }
  1034.     }
  1035.  
  1036.     for(; i < 12; i++)
  1037.       rb_keymenu.keys[i].name = NULL;
  1038.  
  1039.     ps_global->mangled_footer = 1;
  1040.  
  1041.     draw_radio_prompt(real_line, RAD_BUT_COL, q);
  1042.  
  1043.     while(1){
  1044.         fflush(stdout);
  1045.  
  1046.     /*---- Paint the keymenu ----*/
  1047.     EndInverse();
  1048.     draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
  1049.              1 - FOOTER_ROWS(ps_global), 0, FirstMenu, 0);
  1050.     StartInverse();
  1051.     MoveCursor(real_line, RAD_BUT_COL + strlen(q));
  1052.  
  1053.     if(flags & RB_FLUSH_IN)
  1054.       flush_input();
  1055.  
  1056.   newcmd:
  1057.     /* Timeout 5 min to keep imap mail stream alive */
  1058.         ch = read_char(600);
  1059.         dprint(2, (debugfile,
  1060.                    "Want_to read: %s (%d)\n", pretty_command(ch), ch));
  1061.     if(isascii(ch) && isupper(ch))
  1062.       ch = tolower(ch);
  1063.  
  1064.     if(F_ON(F_USE_FK,ps_global)
  1065.        && ((isascii(ch) && isalpha(ch) && !strchr("YyNn", ch))
  1066.            || ((ch >= PF1 && ch <= PF12)
  1067.            && (ch = fkey_table[ch - PF1]) == NO_OP_COMMAND))){
  1068.         /*
  1069.          * The funky test above does two things.  It maps
  1070.          * esc_list character commands to function keys, *and* prevents
  1071.          * character commands from input while in function key mode.
  1072.          * NOTE: this breaks if we ever need more than the first
  1073.          * twelve function keys...
  1074.          */
  1075.         if(flags & RB_ONE_TRY){
  1076.         ch = on_ctrl_C;
  1077.             goto out_of_loop;
  1078.         }
  1079.         Writechar(BELL, 0);
  1080.         continue;
  1081.     }
  1082.  
  1083.         switch(ch) {
  1084.  
  1085.           default:
  1086.         for(i = 0; esc_list && esc_list[i].ch != -1; i++)
  1087.           if(ch == esc_list[i].ch){
  1088.           int len, n;
  1089.  
  1090.           MoveCursor(real_line, len = RAD_BUT_COL + strlen(q));
  1091.           for(n = 0, len = ps_global->ttyo->screen_cols - len;
  1092.               esc_list[i].label[n] && len > 0;
  1093.               n++, len--)
  1094.             Writechar(esc_list[i].label[n], 0);
  1095.  
  1096.           ch = esc_list[i].rval;
  1097.           goto out_of_loop;
  1098.           }
  1099.  
  1100.         if(flags & RB_ONE_TRY){
  1101.         ch = on_ctrl_C;
  1102.             goto out_of_loop;
  1103.         }
  1104.         Writechar(BELL, 0);
  1105.         break;
  1106.  
  1107.           case ctrl('M'):
  1108.           case ctrl('J'):
  1109.             ch = dflt;
  1110.             goto out_of_loop;
  1111.  
  1112.           case ctrl('C'):
  1113.         if(on_ctrl_C || (flags & RB_ONE_TRY)){
  1114.         ch = on_ctrl_C;
  1115.         goto out_of_loop;
  1116.         }
  1117.  
  1118.         Writechar(BELL, 0);
  1119.         break;
  1120.  
  1121.  
  1122.           case '?':
  1123.           case ctrl('G'):
  1124.         if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
  1125.         km_popped++;
  1126.         FOOTER_ROWS(ps_global) = 3;
  1127.         line = -3;
  1128.         real_line = ps_global->ttyo->screen_rows + line;
  1129.         clearfooter(ps_global);
  1130.         draw_radio_prompt(real_line, RAD_BUT_COL, q);
  1131.         break;
  1132.         }
  1133.  
  1134.         if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
  1135.         mark_keymenu_dirty();
  1136.         MoveCursor(real_line + 1, RAD_BUT_COL);
  1137.         CleartoEOLN();
  1138.         MoveCursor(real_line + 2, RAD_BUT_COL);
  1139.         CleartoEOLN();
  1140.         radio_help(real_line, RAD_BUT_COL, help_text);
  1141.         sleep(5);
  1142.         MoveCursor(real_line, RAD_BUT_COL + strlen(q));
  1143.         }
  1144.         else
  1145.           Writechar(BELL, 0);
  1146.  
  1147.             break;
  1148.             
  1149.  
  1150.           case NO_OP_COMMAND:
  1151.         goto newcmd;        /* misunderstood escape? */
  1152.         break;            /* never reached */
  1153.  
  1154.           case NO_OP_IDLE:        /* UNODIR, keep the stream alive */
  1155.         if(flags & RB_NO_NEWMAIL)
  1156.           goto newcmd;
  1157.         else if(new_mail(0, 2, 0) < 0)
  1158.               break;            /* no changes, get on with life */
  1159.             /* Else fall into redraw to adjust displayed numbers and such */
  1160.  
  1161.  
  1162.           case KEY_RESIZE:
  1163.           case ctrl('L'):
  1164.             real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
  1165.             EndInverse();
  1166.             ClearScreen();
  1167.             redraw_titlebar();
  1168.             if(ps_global->redrawer != NULL)
  1169.               (*ps_global->redrawer)();
  1170.             redraw_keymenu();
  1171.             draw_radio_prompt(real_line, RAD_BUT_COL, q);
  1172.             break;
  1173.  
  1174.         } /* switch */
  1175.     }
  1176.  
  1177.   out_of_loop:
  1178.     fs_give((void **)&q);
  1179.     if(ds)
  1180.       fs_give((void **)&ds);
  1181.  
  1182.     EndInverse();
  1183.     fflush(stdout);
  1184.     resume_busy_alarm();
  1185.     if(km_popped){
  1186.     FOOTER_ROWS(ps_global) = 1;
  1187.     clearfooter(ps_global);
  1188.     ps_global->mangled_body = 1;
  1189.     }
  1190.  
  1191.     return(ch);
  1192. }
  1193.  
  1194.  
  1195. /*----------------------------------------------------------------------
  1196.  
  1197.   ----*/
  1198. void
  1199. radio_help(line, column, help)
  1200.      int line, column;
  1201.      HelpType help;
  1202. {
  1203.     char **text;
  1204.  
  1205. #if defined(HELPFILE)
  1206.     text = get_help_text(help, NULL);
  1207. #else
  1208.     text = help;
  1209. #endif
  1210.     if(text == NULL)
  1211.       return;
  1212.     
  1213.     EndInverse();
  1214.     MoveCursor(line + 1, column);
  1215.     CleartoEOLN();
  1216.     if(text[0])
  1217.       PutLine0(line + 1, column, text[0]);
  1218.  
  1219.     MoveCursor(line + 2, column);
  1220.     CleartoEOLN();
  1221.     if(text[1])
  1222.       PutLine0(line + 2, column, text[1]);
  1223.  
  1224.     StartInverse();
  1225. #if defined(HELPFILE)
  1226.     free_help_text(text);
  1227. #endif
  1228.     fflush(stdout);
  1229. }
  1230.  
  1231.  
  1232. /*----------------------------------------------------------------------
  1233.    Paint the screen with the radio buttons prompt
  1234.   ----*/
  1235. void
  1236. draw_radio_prompt(l, c, q)
  1237.     int       l, c;
  1238.     char     *q;
  1239. {
  1240.     int x;
  1241.  
  1242.     StartInverse();
  1243.     PutLine0(l, c, q);
  1244.     x = c + strlen(q);
  1245.     MoveCursor(l, x);
  1246.     while(x++ < ps_global->ttyo->screen_cols)
  1247.       Writechar(' ', 0);
  1248.     MoveCursor(l, c + strlen(q));
  1249.     fflush(stdout);
  1250. }
  1251.